package com.smmisia.article.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.smmisia.article.entity.Point;
import com.smmisia.article.entity.PointStream;
import com.smmisia.article.entity.PointTransaction;
import com.smmisia.article.exception.customException;
import com.smmisia.article.mapper.PointMapper;
import com.smmisia.article.mapper.PointStreamMapper;
import com.smmisia.article.mapper.PointTransactionMapper;
import com.smmisia.article.mapper.UserMapper;
import com.smmisia.article.response.SignResponse;
import com.smmisia.article.service.PointService;
import com.smmisia.article.utils.LoginUtil;
import com.smmisia.common.Result.Result;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;


@Service
public class PointServiceImpl extends ServiceImpl<PointMapper, Point>
        implements PointService {

    //TODO 暂未完成数据持久化

    private static final String SIGN_KEY_PREFIX = "sign:";

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private PointMapper pointMapper;

    @Autowired
    private PointTransactionMapper pointTransactionMapper;

    @Autowired
    private PointStreamMapper pointStreamMapper;

    @Autowired
    private UserMapper userMapper;

    @Override
    public Result<String> sign() {
        String loginId = (String) StpUtil.getLoginId();
        LocalDateTime now = LocalDateTime.now();
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = SIGN_KEY_PREFIX + loginId + keySuffix;
        int dayOfMonth = now.getDayOfMonth();

        LambdaQueryWrapper<Point> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(Point::getUserId, loginId);
        Point exitsPoint = pointMapper.selectOne(wrapper);
        if (exitsPoint == null) {
            Point point = new Point();
            point.setUserId(Integer.valueOf(loginId));
            point.setPoint(1);
            point.setUpdateTime(LocalDateTime.now());
            pointMapper.insert(point);
            PointTransaction pointTransaction = new PointTransaction();
            pointTransaction.setUserId(Integer.valueOf(loginId));
            pointTransaction.setChangeCount(1);
            pointTransaction.setChangeType(0);
            pointTransaction.setDescription("用户签到在" + LocalDateTime.now() + "签到,积分+1");
            pointTransaction.setCreateTime(LocalDateTime.now());
            int insert = pointTransactionMapper.insert(pointTransaction);
            Assert.isTrue(insert == 1, () -> new customException("添加积分记录失败"));
            redisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
            return Result.ok("签到成功");
        }
        if(exitsPoint.getUpdateTime().toLocalDate().equals(now.toLocalDate())){
            return Result.fail("今日已签到");
        }
        exitsPoint.setPoint(exitsPoint.getPoint() + 1);
        pointMapper.updateById(exitsPoint);
        PointTransaction pointTransaction = new PointTransaction();
        pointTransaction.setUserId(Integer.valueOf(loginId));
        pointTransaction.setChangeCount(1);
        pointTransaction.setChangeType(0);
        pointTransaction.setDescription("用户签到在" + LocalDateTime.now() + "签到,积分+1");
        pointTransaction.setCreateTime(LocalDateTime.now());
        int insert = pointTransactionMapper.insert(pointTransaction);
        Assert.isTrue(insert > 0, () -> new customException("添加积分记录失败"));
        redisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
        return Result.ok("签到成功");
    }

    @Override
    public Result<String> signTotal() {
        String loginId = (String) StpUtil.getLoginId();
        LocalDateTime now = LocalDateTime.now();
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = SIGN_KEY_PREFIX + loginId + keySuffix;
        int dayOfMonth = now.getDayOfMonth();
        List<Long> result = redisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );
        // 5.1 判断是否存在签到记录
        if (result == null || result.isEmpty()) {
            // 没有任何签到结果
            return Result.ok(0);
        }
        // 5.2 取获取到的一个签到结果集
        Long num = result.get(0);
        if (num == null || num == 0) {
            // 没有签到结果
            return Result.ok(0);
        }
        // 6.循环遍历
        int count = 0;
        while (true) {
            // 6.1.让这个数字与1做与运算，得到数字的最后一个bit位  // 判断这个bit位是否为0
            if ((num & 1) == 0) {
                // 如果为0，说明未签到，结束
                break;
            } else {
                // 如果不为0，说明已签到，计数器+1
                count++;
            }
            // 把数字右移一位，抛弃最后一个bit位，继续下一个bit位
            num >>>= 1;
        }
        return Result.ok(count);
    }

    @Override
    public Result<SignResponse> getSign() {
        Integer signTotal;
        String loginId = (String) StpUtil.getLoginId();
        LocalDateTime now = LocalDateTime.now();
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = SIGN_KEY_PREFIX + loginId + keySuffix;
        int dayOfMonth = now.getDayOfMonth();
        SignResponse signResponse = new SignResponse();
        List<Long> result = redisTemplate.opsForValue().bitField(
                key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );
        // 5.1 判断是否存在签到记录
        if (result == null || result.isEmpty()) {
            // 没有任何签到结果
            signResponse.setSignTotal(0);
        }
        // 5.2 取获取到的一个签到结果集
        Long num = result.get(0);
        if (num == null || num == 0) {
            // 没有签到结果
            signResponse.setSignTotal(0);
        }
        // 6.循环遍历
        int count = 0;
        while (true) {
            // 6.1.让这个数字与1做与运算，得到数字的最后一个bit位  // 判断这个bit位是否为0
            if ((num & 1) == 0) {
                // 如果为0，说明未签到，结束
                break;
            } else {
                // 如果不为0，说明已签到，计数器+1
                count++;
            }
            // 把数字右移一位，抛弃最后一个bit位，继续下一个bit位
            num >>>= 1;
        }
        Point point = pointMapper.selectOne(Wrappers.lambdaQuery(Point.class).eq(Point::getUserId, loginId));

        signResponse.setSignTotal(count);
        signResponse.setPoint(point.getPoint());
        List<PointStream> pointStreams = pointStreamMapper.selectList(Wrappers.lambdaQuery(PointStream.class).eq(PointStream::getUserId, loginId).orderByDesc(PointStream::getCreateTime));
        signResponse.setPointStreamList(pointStreams);
        return Result.ok(signResponse);
    }

}




